/**
 * 
 */
package com.orcwar.gui;

import static com.orcwar.engine.OWConstants.TILE_SIZE;

import java.util.List;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.Log;

import com.orcwar.controller.OWController;
import com.orcwar.engine.OWConstants;
import com.orcwar.engine.OWGame;
import com.orcwar.engine.OWPlayer;
import com.orcwar.engine.OWTile;
import com.orcwar.engine.OWUnit;
import com.orcwar.enums.SpriteEnum;
import com.orcwar.gui.animations.OWAttackAnimator;

/**
 * @author Callil
 *
 */
public class OWGUIDrawer {
	
	private static OWGUIDrawer instance;

	/**
	 * The game engine. 
	 */
	private OWGame game;

	/**
	 * The GUI manager.
	 */
	private OWGUI gui;
	
	/**
	 * The controller.
	 */
	private OWController controller;
	
	//Temp variables
	OWTile selectedUnitTile = null;
	
	
	//Paints
	private Paint tileIDPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	private Paint tilePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	private Paint unitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	private LightingColorFilter unitEndTurnMask = new LightingColorFilter(
			Color.rgb(0, 0, 0), Color.rgb(0, 0, 0));
	
	
	
	public synchronized static void initialize(OWGame game, OWGUI gui,
			OWController controller) {
		if (instance == null) {
			instance = new OWGUIDrawer(game, gui, controller);
		}
	}
	
	public static OWGUIDrawer getInstance() {
		if (instance == null) {
			Log.e(OWGUIDrawer.class.getName(), "FUCK YOU ! - Initialize first");
		}
		return instance;
	}
	

	/**
	 * Constructor
	 */
	private OWGUIDrawer(OWGame game, OWGUI gui, OWController controller) {
		this.game = game;
		this.gui = gui;
		this.controller = controller;
	}
	
	
	/**
	 * Draw one frame.
	 * @param canvas the canvas to draw on
	 */
	public void draw(Canvas canvas) {
		// Draw the map first
		drawMap(canvas);

		// Then draw the player (which will draw the units).
		drawPlayers(canvas);

		// Draw the foreground elements of the map
		drawForeground(canvas);
		
		// Draw the attack animations
		drawAttacks(canvas);
	}
	
	
	//*****************//
	// PRIVATE METHODS //
	
	/**
	 * Draw the background of the game :
	 * - the tiles (grass, wall, water)
	 * - The highlight of where a unit can move.
	 * @param canvas the canvas
	 */
	private void drawMap(Canvas canvas) {
		for (List<OWTile> tiles : game.getMap().getTiles()) {
			for (OWTile tile : tiles) {
				//Draw one tile
				
				tilePaint.setStyle(Style.FILL);

				// Draw the background
				canvas.drawBitmap(SpriteEnum.getTileBitmap(tile.getTileType()), null, tile.getPosition(),
						tilePaint);
				
				if (!gui.isAttackModeEnabled()) {
					// Check if the tile is currently highlighted for movements
					if (gui.getTilesToHighlighMove().contains(tile)) {
						canvas.drawBitmap(SpriteEnum.TILE_MOVE.getBitmap(), null, tile.getPosition(), null);
					}
				}
				
			}
		}
	}
	
	/**
	 * Draw all that belongs to the players :
	 *  - so far it's juste the units
	 * @param canvas the canvas
	 */
	private void drawPlayers(Canvas canvas) {
		for (OWPlayer player : game.getPlayers()) {
			for (OWUnit unit : player.getUnits()) {
				//Draw one unit
				
				unitPaint.setStyle(Style.FILL);
				// Check if the unit's turn has ended
				if (unit.isTurnEnded()) {
					unitPaint.setColorFilter(unitEndTurnMask);
				} else {
					unitPaint.setColorFilter(null);
				}

				unit.getSprite().draw(canvas, unit.getPosition(), unitPaint, unit.getOrientation());
			}
		}
	}
	
	/**
	 * Draw all the foreground of the map
	 *  - the attack indicator (if needed)
	 *  - the walk path indicator (if needed)
	 *  - the selected unit marker (if needed)
	 *  - the id of the tile for debug only
	 * @param canvas
	 */
	private void drawForeground(Canvas canvas) {
		for (List<OWTile> tiles : game.getMap().getTiles()) {
			for (OWTile tile : tiles) {
				//Draw one tile

				if (controller.getSelectedUnit() != null) {
					selectedUnitTile = controller.getSelectedUnit().getTile();
				}

				if (gui.isAttackModeEnabled() || (controller != null
						&& controller.getSelectedUnit() != null
						&& controller.getSelectedUnit().isEnemy())) {
					//Check whether it's a tile where the selected unit can attack
					if (gui.getTilesToHighlighAttacks().contains(tile) &&
							!gui.getTilesToDisplayPath().keySet().contains(tile)
							&& selectedUnitTile.getId() != tile.getId()) {
						canvas.drawBitmap(SpriteEnum.TILE_ATTACK.getBitmap(), null, tile.getPosition(), null);
					}
				} else {
					//Display the walk path if needed
					if (gui.getTilesToDisplayPath().keySet().contains(tile)
							&& selectedUnitTile.getId() != tile.getId()) {
						SpriteEnum.TILE_WALK_PATH.draw(canvas, tile.getPosition(), null,
								gui.getTilesToDisplayPath().get(tile));
					}
				}

				// Check the currently selected tile
				if (controller.getSelectedUnit() != null &&
						controller.getSelectedUnit().getTile().getId() == tile.getId()) {
					canvas.drawBitmap(SpriteEnum.TILE_SELECTED_UNIT.getBitmap(), null, tile.getPosition(), null);
				}
				
				if (gui.getOnShowPressedAttackTileIds().contains(tile.getId())) {
					canvas.drawBitmap(SpriteEnum.TILE_ON_SHOW_PRESS_ATTACK.getBitmap(), null, tile.getPosition(), null);
				}


				// Draw the tile ID
				if (OWConstants.DEBUG) {
					tileIDPaint.setColor(0xffffffff);
					canvas.drawText(tile.getIdStr(), tile.getX() * TILE_SIZE + TILE_SIZE / 3, tile.getY() * TILE_SIZE
							+ TILE_SIZE / 3, tileIDPaint);
				}
			}
		}
		
	}
	
	
	private void drawAttacks(Canvas canvas) {
		for (OWAttackAnimator anim : gui.getAttackAnimaters()) {
			anim.draw(canvas);
		}
	}

	// PRIVATE METHODS //
	//*****************//
	
	
	
	//*******************//
	//GETTERS AND SETTERS//
	
	/**
	 * @return the controller
	 */
	public OWController getController() {
		return controller;
	}

	/**
	 * @param controller the controller to set
	 */
	public void setController(OWController controller) {
		this.controller = controller;
	}
	
	//GETTERS AND SETTERS//
	//*******************//
}
